using System;
using System.Collections.Generic;
using System.Linq;
using UserManagement.Dal;
using UserManagement.DomainEntity.Exceptions;

namespace UserManagement.DomainEntity
{
    public class UserAccount
    {
        #region public properties

        /// <summary>
        /// Gets or sets the id.
        /// </summary>
        /// <value>
        /// The unique id of user account.
        /// </value>
        public virtual int? Id { get; private set; }

        /// <summary>
        /// Gets or sets the name of account.
        /// </summary>
        /// <value>The string of account name.</value>
        /// <remarks>The account name should be unique in system</remarks>
        public virtual string Name { get; set; }

        /// <summary>
        /// Gets or sets the password.
        /// </summary>
        /// <value>The string of password.</value>
        /// <remarks>The password of account.</remarks>
        public virtual string Password { get; set; }

        /// <summary>
        /// Gets or sets the contact of account holder.
        /// </summary>
        /// <value>The UserContact</value>
        /// <remarks>The user's contact who hold the account.</remarks>
        public virtual UserContact Contact { get; set; }

        /// <summary>
        /// Gets or sets the circle.
        /// </summary>
        /// <value>The AccountCircle</value>
        /// <remarks>The circle is a group of user account, each circle get unique permission settings.</remarks>
        public virtual IEnumerable<UserCircle> Circles { get; private set; }

        #endregion public properties

        #region public methods
        
        /// <summary>
        /// Create a circle for a <see cref="UserAccount"/>
        /// </summary>
        /// <param name="circle">The circle to be created.</param>
        public virtual void CreateCircle(UserCircle circle)
        {
            circle.Owner = this;
            Circles.ToList().Add(circle);
        }

        #endregion public methods

        #region public static methods

        public static UserAccount AddUserAccount(IRepository repository, UserAccount userAccount)
        {
            string errorMsg;
            if (!IsValidNewUserAccount(repository, userAccount, out errorMsg))
            {
                throw new NewUserAccountInvalidException(errorMsg);
            }

            repository.Add(userAccount);
            return userAccount;
        }

        public static bool IsValidNewUserAccount(IRepository repository, UserAccount account, out string errorMsg)
        {
            if (account == null)
            {
                errorMsg = "Null account";
                return false;
            }

            if (string.IsNullOrEmpty(account.Name))
            {
                errorMsg = "Null or empty userName found";
                return false;
            }

            if (string.IsNullOrEmpty(account.Password))
            {
                errorMsg = "Null or empty password found";
                return false;
            }

            if (account.Contact == null)
            {
                errorMsg = "Null contact for user account";
                return false;
            }

            var contactOk = UserContact.IsValidExistContact(repository, account.Contact, out errorMsg);
            if (!contactOk)
            {
                return false;
            }

            // check to see if user name already exists
            var accInDb = repository.FindEntities<UserAccount>(acc => acc.Name.Trim().Equals(account.Name.Trim(), StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
            bool result;
            if (accInDb != null)
            {
                errorMsg = "User name already exists in system";
                result = false;
            }
            else
            {
                errorMsg = string.Empty;
                result = true;
            }

            return result;
        }

        public static bool IsValidExistUserAccount(IRepository repository, UserAccount account, out string errorMsg)
        {
            if (account == null)
            {
                errorMsg = "Null account";
                return false;
            }

            if (account.Id == null)
            {
                errorMsg = "UserAccount is invalid, null id found";
                return false;
            }

            //// check to see if user name already exists
            //var accInDb = repository.FindEntities<IUserAccount>(acc => acc.Name.Trim().Equals(account.Name.Trim(), StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
            //bool result;
            //if (accInDb != null)
            //{
            //    errorMsg = "User name already exists in system";
            //    result = false;
            //}
            //else
            //{
            //    errorMsg = string.Empty;
            //    result = true;
            //}

            errorMsg = string.Empty;
            return true;
        }

        public static UserAccount FindUserByName(IRepository repository, string userName)
        {
            return repository.FindEntities<UserAccount>(acc => acc.Name.Trim().Equals(userName.Trim(), StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
        }

        public static UserAccount Update(IRepository repository, UserAccount userAccount)
        {
            var account = FindUserByName(repository, userAccount.Name);
            if (account == null)
            {
                // cannot find user from repository, just return without any error
                return null;
            }

            try
            {
                return repository.Update(userAccount);
            }
            catch (Exception ex)
            {
                throw;
            }
        }

        public static UserAccount Login(IRepository repository, string name, string password)
        {
            var accounts =
                repository.FindEntities<UserAccount>(acc => acc.Name == name && acc.Password == password);
            if (accounts != null)
            {
                return accounts.FirstOrDefault();
            }

            return null;
        }

        #endregion public static methods
    }
}